﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Microsoft.Office.Interop.Outlook;

namespace OSC.Base
{
    public class IsolatedStorageCache : ICachingStrategy
    {
        private class Mapping
        {
            public string UserId { get; private set; }
            public string EmailAddress { get; private set; }
            public string Hashed { get; private set; }

            public Mapping(string userId, string emailAddress)
            {
                UserId = userId;
                EmailAddress = emailAddress;
                var md5 = new MD5CryptoServiceProvider();
                var bytes = md5.ComputeHash(Encoding.ASCII.GetBytes(EmailAddress));
                Hashed = string.Join("", bytes.Select(b => b.ToString("X2")).ToArray()).ToLower();
            }
        }

        private readonly IObjectContainer m_Container;
        private ISocialConnector m_Session;
        private int m_synchRestartInterval;

        public IsolatedStorageCache(Guid providerId, ISocialConnector session, int synchRestartInterval)
        {
            var store = IsolatedStorageFile.GetUserStoreForAssembly();
            var filename = providerId + ".sto";


            var fi = store.GetType().GetField("m_RootDir", BindingFlags.NonPublic | BindingFlags.Instance);
            var path = (string)fi.GetValue(store);

            File.Delete(path + filename);


            m_Container = Db4oFactory.OpenFile(path + filename);

            m_Session = session;

            if (synchRestartInterval == 0) m_synchRestartInterval = 600;
            else m_synchRestartInterval = synchRestartInterval;
        }

        #region Methods
        public IPerson GetPerson(string userId)
        {
            return m_Container.Cast<IPerson>().FirstOrDefault(p => p.UserId.Equals(userId));
        }
        public IEnumerable<IPerson> FindPerson(string userId)
        {
            return m_Container.Cast<IPerson>().Where(p => p.UserId.Equals(userId)).ToList();
        }
        public string GetUserId(string[] hashedAddress)
        {
            var users = m_Container.Cast<Mapping>().Where(m => hashedAddress.Contains(m.Hashed)).ToList();
            var user = users.FirstOrDefault();
            if (user == null)
                return string.Empty;

            ActivitiesUpdater(m_Container.Cast<IPerson>().Where(p => p.UserId == user.UserId).ToList());
            return user.UserId;
        }
        public IEnumerable<IActivity> GetActivities(string userId, DateTime startTime)
        {
            return m_Container.Cast<IActivity>().Where(a => a.Owner.UserId == userId && a.PublishDate >= startTime);
        }
        public IEnumerable<IPerson> GetFriends(string userId)
        {
            return m_Container.Cast<IPerson>();
        }
        public IPerson Logon(string userName, string password, out string connectOut)
        {
            var person = m_Session.Logon(userName, password, out connectOut);
            StorePersonInContainer(person);

            //new Timer(o =>{}, null, 6000, m_synchRestartInterval * 1000);
            FriendsUpdater();
            return person;
        }
        public void Follow(string[] emailAddresses, string displayName)
        {
            //throw new NotImplementedException();
        }
        private void StorePersonInContainer(IPerson person)
        {
            var pers = m_Container.Cast<IPerson>().SingleOrDefault(p => p.UserId == person.UserId);
            if (pers == null)
            {
                GetEmailAddressFromOutlook(person.FullName).Where(s => !string.IsNullOrEmpty(s)).ToList().ForEach(person.EmailAddresses.Add);
                person.EmailAddresses.ToList().ForEach(email => m_Container.Store(new Mapping(person.UserId, email)));
                m_Container.Store(person);
                m_Container.Commit();
            }
        }
        private void FriendsUpdater()
        {
            var friends = m_Session.GetFriends().ToList();
            friends.ForEach(StorePersonInContainer);
        }
        private void ActivitiesUpdater(List<IPerson> users)
        {
            users.Select(user => m_Session.GetActivities(user)).SelectMany(a => a)
                     .Where(a => !m_Container.Cast<IActivity>().Any(act => a.Id == act.Id))
                     .ToList()
                     .ForEach(a => m_Container.Store(a));
            m_Container.Commit();
        }
        private string[] GetEmailAddressFromOutlook(string fullname)
        {
            var app = new Application();
            var cnts = app.Session.GetDefaultFolder(OlDefaultFolders.olFolderContacts);
            var emailAddresses = cnts.Items.OfType<ContactItem>().Where(c => c != null && c.FullName != null)
                                    .Where(cnt => cnt.FullName.Equals(fullname, StringComparison.CurrentCultureIgnoreCase))
                .Select(cnt => new[]
                                   {
                                       GetEmailBasedOnType(cnt.Email1Address, cnt.Email1DisplayName, cnt.Email1AddressType),
                                       GetEmailBasedOnType(cnt.Email2Address, cnt.Email2DisplayName, cnt.Email2AddressType),
                                       GetEmailBasedOnType(cnt.Email3Address, cnt.Email3DisplayName, cnt.Email3AddressType),
                                   })
                .SelectMany(m => m).ToArray();

            return emailAddresses;
        }
        private string GetEmailBasedOnType(string emailAddress, string displayName, string type)
        {
            if (emailAddress == null) return string.Empty;
            if (type != "SMTP")
            {
                var index = displayName.IndexOf('(') + 1;
                return displayName.Substring(index, displayName.Length - index - 1);
            }
            return emailAddress;
        }
        #endregion
    }
}